#include "TableStat.h"
#include "../sql/ast/expr/SQLPropertyExpr.h"

TableStat_Mode_ptr TableStat_Mode::Insert      = TableStat_Mode_ptr(new TableStat_Mode(1));        //
TableStat_Mode_ptr TableStat_Mode::Update      = TableStat_Mode_ptr(new TableStat_Mode(2));        //
TableStat_Mode_ptr TableStat_Mode::Delete      = TableStat_Mode_ptr(new TableStat_Mode(4));        //
TableStat_Mode_ptr TableStat_Mode::Select      = TableStat_Mode_ptr(new TableStat_Mode(8));        //
TableStat_Mode_ptr TableStat_Mode::Merge       = TableStat_Mode_ptr(new TableStat_Mode(16));        //
TableStat_Mode_ptr TableStat_Mode::Truncate    = TableStat_Mode_ptr(new TableStat_Mode(32));     //
TableStat_Mode_ptr TableStat_Mode::Alter       = TableStat_Mode_ptr(new TableStat_Mode(64));        //
TableStat_Mode_ptr TableStat_Mode::Drop        = TableStat_Mode_ptr(new TableStat_Mode(128));        //
TableStat_Mode_ptr TableStat_Mode::DropIndex   = TableStat_Mode_ptr(new TableStat_Mode(256));   //
TableStat_Mode_ptr TableStat_Mode::CreateIndex = TableStat_Mode_ptr(new TableStat_Mode(512)); //
TableStat_Mode_ptr TableStat_Mode::Replace     = TableStat_Mode_ptr(new TableStat_Mode(1024));
TableStat_Mode_ptr TableStat_Mode::DESC        = TableStat_Mode_ptr(new TableStat_Mode(2048)); //

TableStat_Mode::TableStat_Mode(int mark)
{
  this->mark = mark;
}

bool TableStat_Mode::operator==(const TableStat_Mode_ptr ps) const
{
  if (this->mark == ps->mark)
    return true;
  return false;
}

TableStat_Name::TableStat_Name(string_ptr name)
{
  TableStat_Name(name, FnvHash::hashCode64(name));
}

TableStat_Name::TableStat_Name(string_ptr name, long hashCode64_)
{
  this->name = name;
  this->hashCode64_ = hashCode64_;
}

string_ptr TableStat_Name::getName()
{
  return this->name;
}

int TableStat_Name::hashCode()
{
  long value = hashCode64();
  return (int)(value ^ (value >> 32));
}

long TableStat_Name::hashCode64()
{
  return hashCode64_;
}

bool TableStat_Name::equals(Object_ptr o)
{
  // if (!(o instanceof Name)) {
  //     return false;
  // }

  TableStat_Name *other = std::dynamic_pointer_cast<TableStat_Name>(o).get();
  return this->hashCode64_ == other->hashCode64_;
}

string_ptr TableStat_Name::toString()
{
  string_ptr tmp = SQLUtils::normalize(this->name);
  return (tmp);
}

TableStat_Relationship::TableStat_Relationship(std::shared_ptr<TableStat_Column> left, std::shared_ptr<TableStat_Column> right, string_ptr operator_)
{
  this->left = left;
  this->right = right;
  this->operator_ = operator_;
}

std::shared_ptr<TableStat_Column> TableStat_Relationship::getLeft()
{
  return left;
}

std::shared_ptr<TableStat_Column> TableStat_Relationship::getRight()
{
  return right;
}

string_ptr TableStat_Relationship::getOperator()
{
  return operator_;
}

int TableStat_Relationship::hashCode()
{
  int prime = 31;
  int result = 1;
  result = prime * result + ((left == NULL) ? 0 : left->hashCode());
  // result = prime * result + ((operator_ == NULL) ? 0 : operator.hashCode());
  result = prime * result + ((right == NULL) ? 0 : right->hashCode());
  return result;
}

string_ptr TableStat_Relationship::getClass()
{
  return make_string_ptr("TableStat_Relationship");
}

bool TableStat_Relationship::equals(Object_ptr obj)
{
  if (this == std::dynamic_pointer_cast<TableStat_Relationship>(obj).get())
  {
    return true;
  }
  if (obj == NULL)
  {
    return false;
  }
  if (getClass() != obj->getClass())
  {
    return false;
  }
  TableStat_Relationship *other = std::dynamic_pointer_cast<TableStat_Relationship>(obj).get();
  if (left == NULL)
  {
    if (other->left != NULL)
    {
      return false;
    }
  }
  else if (!left->equals(std::dynamic_pointer_cast<Object>(other->left)))
  {
    return false;
  }
  if (operator_->empty())
  {
    if (!other->operator_->empty())
    {
      return false;
    }
  }
  else if (operator_ != other->operator_)
  {
    return false;
  }
  if (right == NULL)
  {
    if (other->right != NULL)
    {
      return false;
    }
  }
  else if (!right->equals(std::dynamic_pointer_cast<TableStat_Column>(other->right)))
  {
    return false;
  }
  return true;
}

string_ptr TableStat_Relationship::toString()
{
  string_ptr tmp;
  tmp->append(*left->getName());
  tmp->append(" ");
  tmp->append(*operator_);
  tmp->append(" ");
  tmp->append(*right->getName());
  return tmp;
}

TableStat_Condition::TableStat_Condition(std::shared_ptr<TableStat_Column> column, string_ptr operator_)
{
  this->column = column;
  this->operator_ = operator_;
  values = std::make_shared<std::list<Object_ptr>>();
}

std::shared_ptr<TableStat_Column> TableStat_Condition::getColumn()
{
  return column;
}

string_ptr TableStat_Condition::getOperator()
{
  return operator_;
}

Object_list_ptr TableStat_Condition::getValues()
{
  return values;
}

void TableStat_Condition::addValue(Object_ptr value)
{
  this->values->push_back(value);
}

int TableStat_Condition::hashCode()
{
  int prime = 31;
  int result = 1;
  result = prime * result + ((column == NULL) ? 0 : column->hashCode());
  // result = prime * result + ((operator_ == NULL) ? 0 : operator.hashCode());
  return result;
}

bool TableStat_Condition::equals(Object_ptr obj)
{
  if (this == obj.get())
  {
    return true;
  }
  if (obj == NULL)
  {
    return false;
  }
  if (getClass() != obj->getClass())
  {
    return false;
  }
  TableStat_Condition *other = std::dynamic_pointer_cast<TableStat_Condition>(obj).get();
  if (column == NULL)
  {
    if (other->column != NULL)
    {
      return false;
    }
  }
  else if (!column->equals(std::dynamic_pointer_cast<TableStat_Column>(other->column)))
  {
    return false;
  }
  if (operator_->empty())
  {
    if (!other->operator_->empty())
    {
      return false;
    }
  }
  else if (!operator_->compare(*other->operator_))
  {
    return false;
  }
  return true;
}

string_ptr TableStat_Condition::toString()
{
  string_ptr buf;
  buf->append(this->column->toString()->c_str());
  buf->append(" ");
  buf->append(*this->operator_);

  // if (values.size() == 1) {
  //     buf->append(" ");
  //     buf->append(std::to_string(this->values.pop_back()));
  // } else if (values.size() > 0) {
  //     buf->append(" (");
  //     for (int i = 0; i < values.size(); ++i) {
  //         if (i != 0) {
  //             buf->append(", ");
  //         }
  //         Object_ptr  val = values.get(i);
  //         if (val instanceof string_ptr ) {
  //             string_ptr  jsonStr = JSONUtils.toJSONString(val);
  //             buf->append(jsonStr);
  //         } else {
  //             buf->append(string_ptr .valueOf(val));
  //         }
  //     }
  //     buf->append(")");
  // }

  return (buf);
}

TableStat_Column::TableStat_Column(string_ptr table, string_ptr name)
{
  TableStat_Column(table, name, std::make_shared<DbType>(DbType::other));
}

TableStat_Column::TableStat_Column(string_ptr table, string_ptr name, DbType_ptr dbType)
{
  this->table = table;
  this->name = name;

  int p = table->find('.');
  if (p != -1)
  {
    // if (dbType == NULL)
    // {
    if (table->find('`') != -1)
    {
      dbType = std::make_shared<DbType>(DbType::mysql);
    }
    else if (table->find('[') != -1)
    {
      dbType = std::make_shared<DbType>(DbType::sqlserver);
    }
    else if (table->find('@') != -1)
    {
      dbType = std::make_shared<DbType>(DbType::oracle);
    }
    // }
    SQLExpr_ptr owner = SQLUtils::toSQLExpr(table, dbType);
    SQLPropertyExpr *sqlPropertyExpr = new SQLPropertyExpr(owner, (name));
    hashCode64_ = sqlPropertyExpr->hashCode64();
  }
  else
  {
    hashCode64_ = FnvHash::hashCode64(table, name);
  }
}

TableStat_Column::TableStat_Column(string_ptr table, string_ptr name, long hashCode64_)
{
  this->table = table;
  this->name = name;
  this->hashCode64_ = hashCode64_;
}

string_ptr TableStat_Column::getTable()
{
  return table;
}

string_ptr TableStat_Column::getFullName()
{
  if (fullName->empty())
  {
    if (table->empty())
    {
      fullName = name;
    }
    else
    {
      fullName = make_string_ptr(*table + '.' + *name);
    }
  }

  return fullName;
}

long TableStat_Column::hashCode64()
{
  return hashCode64_;
}

bool TableStat_Column::isWhere()
{
  return where;
}

void TableStat_Column::setWhere(bool where)
{
  this->where = where;
}

bool TableStat_Column::isSelect()
{
  return select;
}

void TableStat_Column::setSelec(bool select)
{
  this->select = select;
}

bool TableStat_Column::isGroupBy()
{
  return groupBy;
}

void TableStat_Column::setGroupBy(bool groupBy)
{
  this->groupBy = groupBy;
}

bool TableStat_Column::isHaving()
{
  return having;
}

bool TableStat_Column::isJoin()
{
  return join;
}

void TableStat_Column::setJoin(bool join)
{
  this->join = join;
}

void TableStat_Column::setHaving(bool having)
{
  this->having = having;
}

bool TableStat_Column::isPrimaryKey()
{
  return primaryKey;
}

void TableStat_Column::setPrimaryKey(bool primaryKey)
{
  this->primaryKey = primaryKey;
}

bool TableStat_Column::isUnique()
{
  return unique;
}

void TableStat_Column::setUnique(bool unique)
{
  this->unique = unique;
}

bool TableStat_Column::isUpdate()
{
  return update;
}

void TableStat_Column::setUpdate(bool update)
{
  this->update = update;
}

string_ptr TableStat_Column::getName()
{
  return name;
}

/**
 * @since 1.0.20
 */
string_ptr TableStat_Column::getDataType()
{
  return dataType;
}

/**
 * @since 1.0.20
 */
void TableStat_Column::setDataType(string_ptr dataType)
{
  this->dataType = dataType;
}

std::shared_ptr<std::map<std::string, Object_ptr>> TableStat_Column::getAttributes()
{
  return attributes;
}

void TableStat_Column::setAttributes(std::shared_ptr<std::map<std::string, Object_ptr>> attributes)
{
  this->attributes = attributes;
}

int TableStat_Column::hashCode()
{
  long hash = hashCode64();
  return (int)(hash ^ (hash >> 32));
}

string_ptr TableStat_Column::toString()
{
  string_ptr tmp;
  if (!table->empty())
  {
    tmp = make_string_ptr(*SQLUtils::normalize(table) + "." + *SQLUtils::normalize(name));
    return tmp;
  }

  tmp = SQLUtils::normalize(name);
  return (tmp);
}

bool TableStat_Column::equals(Object_ptr obj)
{
  // if (!(obj instanceof Column)) {
  //     return false;
  // }

  std::shared_ptr<TableStat_Column> column = std::dynamic_pointer_cast<TableStat_Column>(obj);
  return hashCode64_ == column->hashCode64_;
}

TableStat::TableStat()
{
}

int TableStat::getReferencedCount()
{
  return referencedCount;
}

void TableStat::incrementReferencedCount()
{
  referencedCount++;
}

int TableStat::getDropIndexCount()
{
  return dropIndexCount;
}

void TableStat::incrementDropIndexCount()
{
  this->dropIndexCount++;
}

void TableStat::incrementAddCount()
{
  this->addCount++;
}

int TableStat::getAddCount()
{
  return addCount;
}

void TableStat::incrementAddPartitionCount()
{
  this->addPartitionCount++;
}

int TableStat::getAddPartitionCount()
{
  return addPartitionCount;
}

int TableStat::getCreateIndexCount()
{
  return createIndexCount;
}

void TableStat::incrementCreateIndexCount()
{
  createIndexCount++;
}

int TableStat::getAlterCount()
{
  return alterCount;
}

void TableStat::incrementAlterCount()
{
  this->alterCount++;
}

int TableStat::getCreateCount()
{
  return createCount;
}

void TableStat::incrementCreateCount()
{
  this->createCount++;
}

int TableStat::getMergeCount()
{
  return mergeCount;
}

void TableStat::incrementMergeCount()
{
  this->mergeCount++;
}

int TableStat::getDropCount()
{
  return dropCount;
}

void TableStat::incrementDropCount()
{
  dropCount++;
}

void TableStat::setDropCount(int dropCount)
{
  this->dropCount = dropCount;
}

int TableStat::getSelectCount()
{
  return selectCount;
}

void TableStat::incrementSelectCount()
{
  selectCount++;
}

void TableStat::setSelectCount(int selectCount)
{
  this->selectCount = selectCount;
}

int TableStat::getUpdateCount()
{
  return updateCount;
}

void TableStat::incrementUpdateCount()
{
  updateCount++;
}

void TableStat::setUpdateCount(int updateCount)
{
  this->updateCount = updateCount;
}

int TableStat::getDeleteCount()
{
  return deleteCount;
}

void TableStat::incrementDeleteCount()
{
  this->deleteCount++;
}

void TableStat::setDeleteCount(int deleteCount)
{
  this->deleteCount = deleteCount;
}

void TableStat::incrementInsertCount()
{
  this->insertCount++;
}

int TableStat::getInsertCount()
{
  return insertCount;
}

void TableStat::setInsertCount(int insertCount)
{
  this->insertCount = insertCount;
}

int TableStat::getAnalyzeCount()
{
  return analyzeCount;
}

void TableStat::incrementAnalyzeCount()
{
  this->analyzeCount++;
}

string_ptr TableStat::toString()
{
  string_ptr buf = make_string_ptr("");
  if (mergeCount > 0)
  {
    buf->append("Merge");
  }
  if (insertCount > 0)
  {
    buf->append("Insert");
  }
  if (updateCount > 0)
  {
    buf->append("Update");
  }
  if (selectCount > 0)
  {
    buf->append("Select");
  }
  if (deleteCount > 0)
  {
    buf->append("Delete");
  }
  if (dropCount > 0)
  {
    buf->append("Drop");
  }
  if (createCount > 0)
  {
    buf->append("Create");
  }
  if (alterCount > 0)
  {
    buf->append("Alter");
  }
  if (createIndexCount > 0)
  {
    buf->append("CreateIndex");
  }
  if (dropIndexCount > 0)
  {
    buf->append("DropIndex");
  }
  if (addCount > 0)
  {
    buf->append("Add");
  }
  if (addPartitionCount > 0)
  {
    buf->append("AddPartition");
  }
  if (analyzeCount > 0)
  {
    buf->append("Analyze");
  }

  return (buf);
}
